# Copyright (c) 2008-2009 Mikhail Chernov 
#
# This file is part of dcExplorer.
#
# dcExplorer is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# dcExplorer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with dcExplorer.  If not, see <http://www.gnu.org/licenses/>.

# Scripting class with extensions
package dcExplorer::core::Unit;
use strict;
use utf8;
use core::Object;
use base 'dcExplorer::core::Object';
use WeakRef;
use server::Command;

@dcExplorer::core::Unit::props = 
	qw(id lvl building_id hit_pointsleft tag i_tag unwrapped creator_race max_hitpoints is_ground_unit is_space_ship is_transportable carrier_capacity transport_capacity fly_range fly_speed weight class);

my %parts = (
	1 => "Шаттл",
	2 => "Коммерческий шаттл",
	3 => "Легкий строенный лазер",
	4 => "Средний лазер",
	5 => "Легкая ракета",
	6 => "Броневые листы",
	7 => "Лазер ПРО",
	8 => "Двигатель Итера",
	9 => "Геологоразведчик",
	10 => "Движок-невидимка",
	11 => "Зонд",
	12 => "Малый гипердвигатель",
	13 => "Транспортная ячейка",
	14 => "Звезда Смерти",
	15 => "Двигатель Раша",
	16 => "Спаренный двигатель Раша",
	18 => "Легкий крейсер",
	19 => "Система управления огнем",
	20 => "Катер",
	21 => "Транспорт",
	22 => "Штурмовик",
	23 => "Лаборатория",
	24 => "Пират",
	25 => "Корвет",
	26 => "Микрозонд",
	27 => "Орбитальная крепость",
	28 => "Ниндзя",
	29 => "Военный транспорт",
	30 => "Москит",
	31 => "Сторожевик",
	32 => "Рейсовый транспорт",
	33 => "Ракетный крейсер",
	34 => "Минилаборатория",
	35 => "Летучий Голландец",
	37 => "Планетарный штурмовик",
	38 => "Крейсер",
	43 => "Двигатель Итера легкий",
	44 => "Спаренный двигатель Итера",
	45 => "Сверхлегкий двигатель Итера",
	46 => "Двигатель со вторым резонатором",
	47 => "Строенный экономичный двигатель",
	48 => "Двигатель Пушинка",
	49 => "Форсированный двигатель Раша",
	50 => "Резонатор удвоенной частоты",
	54 => "Плазменная пушка",
	55 => "Тяжелый лазер",
	56 => "Облегченный лазер",
	57 => "Миниатюрный лазер",
	58 => "Гиперлазер",
	59 => "Лазерная турель",
	60 => "Спаренный сверхлегкий лазер",
	61 => "Гигантский лазер",
	62 => "Спаренная транспортная ячейка",
	63 => "Зенитный лазер",
	64 => "Пульсовый лазер",
	65 => "Спаренный пульсовый лазер",
	66 => "Многоцелевой лазер",
	67 => "Гиперлазер-II",
	72 => "Ракета",
	73 => "Игла",
	74 => "Спаренная Игла",
	75 => "Тяжелая ракета",
	76 => "Ракетный комплекс",
	77 => "Залповая ракетная система",
	78 => "Игла-II",
	79 => "Легкая Игла",
	80 => "Сверхтяжелая ракета",
	85 => "Зеркальная лазерная защита",
	86 => "Легкое ПРО",
	87 => "Газовая защита от лазеров",
	88 => "Керамическая броня",
	89 => "Комплексная система защиты",
	90 => "Сверхлегкое ПРО",
	91 => "Усиленная газовая защита",
	92 => "Усиленные лазеры ПРО",
	93 => "Легкая комплексная защита",
	94 => "Системы наведения помех",
	100 => "Детектор",
	101 => "Большой детектор",
	102 => "Дважды невидимка",
	103 => "Портативный детектор",
	104 => "Легкая транспортная ячейка",
	106 => "Система наведения ракет",
	107 => "Обозреватель",
	108 => "Силы самообороны",
	109 => "Пехота",
	110 => "Боевые роботы",
	111 => "Танк",
	112 => "Бункер",
	113 => "Боевые дройды",
	114 => "Батарея ПВО",
	115 => "Ракетная батарея",
	118 => "Тяжелый робот",
	121 => "Бакен",
	122 => "Орбитальное поселение",
	125 => "Тяжелая абордажная команда",
	126 => "Абордажные дроиды",
	131 => "Мобильная станция слежения",
	134 => "Тяжелая пехота",
	135 => "Конверсионный проект",
	137 => "Батарея ПКО",
	141 => "Кибер-дайвер"
);

my %buildings = (
	1 => "Шахта",
	2 => "Биоферма",
	3 => "Генератор",
	4 => "Казармы",
	5 => "Верфь",
	7 => "Колония",
	12 => "Дворец",
	13 => "Наместник",
	14 => "Завод",
	22 => "Таможня",
	23 => "Контейнер",
	24 => "Оприходование",
	25 => "Конструкторское бюро",
	28 => "Фабрика комплектующих",
	30 => "Орбитальное поселение",
	31 => "Уплотнитель стази",
	32 => "Центр исследований",
	33 => "Исследование",
	34 => "Специалист",
	35 => "Мобильный портал",
	36 => "Крестный отец",
	501 => "Beta-tester",
	502 => "Heavy mech engenear",
	503 => "Lasers tooler",
	504 => "Buoyager",
	505 => "Orbital constructor",
	506 => "Leading Orbital Constructor",
	507 => "Boarding instructor",
	508 => "Good boarding instructor",
	509 => "Boarding droids Constructor",
	510 => "Scrap collection specialist",
	511 => "Scrap collection advanced specialist",
	512 => "Asteroids collection specialist",
	513 => "Asteroids collection advanced specialist",
	514 => "Transportable detectors engenear",
	515 => "Former pirate",
	516 => "Former smuggler",
	517 => "Heavy infantry instructor",
	518 => "Convertions specialist",
	519 => "Surface-to-Space systems constructor",
	520 => "Boring specialist",
	521 => "Advanced boring specialist",
	522 => "Mining optimisation expert",
	523 => "Unknown specialist",
	524 => "Unknown specialist",
	525 => "Unknown specialist",
	526 => "Unknown specialist",
	527 => "Unknown specialist",
	528 => "Unknown specialist",
	529 => "Unknown specialist",
	530 => "Unknown specialist",
	531 => "Unknown specialist",
	532 => "Unknown specialist",
	533 => "Unknown specialist",
	534 => "Unknown specialist",
	535 => "Unknown specialist",
	536 => "Unknown specialist",
	537 => "Unknown specialist",
	538 => "Unknown specialist",
	539 => "Unknown specialist",
	540 => "Unknown specialist",
	541 => "Unknown specialist",
	542 => "Unknown specialist",
	543 => "Unknown specialist",
	544 => "Unknown specialist",
	545 => "Unknown specialist",
	546 => "Unknown specialist",
	547 => "Unknown specialist",
	548 => "Unknown specialist",
	549 => "Unknown specialist"
#	503 => "Настройщик лазеров",
#	504 => "Бакенщик",
#	515 => "Бывший Пират"
);

# Construction methods

sub new 
{
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $self = $class->SUPER::new;
	weaken($self->{Parent} = shift);
	$self->{'id'} = shift;
	$self->{Actions} = [];
	bless ($self, $class);
	return $self;
}

sub setActions
{
	my $self = shift;
	my $actions = shift;
	$self->{Actions} = $actions;
}

# Supplimentary methods

sub getPartName
{
	my $id = shift;
	return defined($parts{$id}) ? $parts{$id} : "<Деталь ".$id.">";
}

sub getBuildingName
{
	my $id = shift;
	return defined($buildings{$id}) ? $buildings{$id} : "<Здание ".$id.">";
}

sub getTypeName
{
	my $self = shift;
	return $self->getProp('building_id') > 1000 ?
			getPartName($self->getProp('carapace')) :
			getBuildingName($self->getProp('building_id'));
}

sub getPersonalName
{
	my $self = shift;
	if ($self->getProp('building_id') == 23)
	{
		my ($type) = $self->getProp('tag') =~ /^<(itm-in-pack|res-in-pack|part)\s/;
		if ($type eq 'itm-in-pack')
		{
			my ($name) = $self->getProp('tag') =~ /name="([^"]*)"/;
			return $name;
		}
		if ($type eq 'res-in-pack')
		{
			my ($res) = $self->getProp('tag') =~ /res="([^"]*)"/;
			my ($amount) = $self->getProp('tag') =~ /amount="([^"]*)"/;
			return $amount.$res;
		}
		if ($type eq 'part')
		{
			my ($lvl) = $self->getProp('tag') =~ /lvl="([^"]*)"/;
			my ($id) = $self->getProp('tag') =~ /id="([^"]*)"/;
			my ($race) = $self->getProp('tag') =~ /race="([^"]*)"/;
			return getPartName($id)."-".$lvl." от ".$race;
		}
		# This is for debugging purposes. Should be removed before final release.
		return $self->getProp('tag');
	}
	else
	{
		return $self->getProp('name');
	}
}

sub getShortName
{
	my $self = shift;
	return defined($self->getProp('name')) ? $self->getProp('name') : $self->getTypeName;
}

sub getDisplayName
{
	my $self = shift;
	my $personalName = $self->getPersonalName;
	my $typeName = $self->getTypeName;
	return $typeName.(defined($personalName) && $personalName ne $typeName ? ' "'.$personalName.'"' : '');
}

# Internal engine methods

sub getEmpire
{
	my $self = shift;
	return $self->{Parent}->getEmpire();
}

# Scripting interface extension

sub cancelAction
{
	my $self = shift;
	my $actionId = shift;
	my ($action) = grep {$_->{'action'} == $actionId} @{$self->{Actions}};
	if ($action && $action->{'request_id'})
	{
		$self->getEmpire()->commandQueue()->enqueue(dcExplorer::server::Command->new('cancel_action', 
			{'unit_id' => $self->getProp('id'), 'action_id' => $action->{'request_id'}}));
		delete $action->{'request_id'};
		return 1;
	}
	else
	{
		return undef;
	}
}

# Scripting interface

sub performAction
{
	my $self = shift;
	my $actionId = shift;
	return undef if (scalar grep {defined($_->{'request_id'})} @{$self->{Actions}});
	my ($action) = grep {$_->{'action'} == $actionId} @{$self->{Actions}};
	# TODO: Add a mechnism to set the id correctly.
	$action->{'request_id'} = 0;
	$self->getEmpire()->commandQueue()->enqueue(dcExplorer::server::Command->new('store_action', 
  		{'unit_id' => $self->getProp('id'), 'action_id' => $actionId}));
	return 1;
}

sub transferToAnotherFleet
{
	my $self = shift;
	my $fleetId = shift;
	my ($x, $y) = map {$self->{Parent}->getProp($_)} qw(x y);
	if ($fleetId)
	{
		my $fleet = $self->getEmpire()->fleets->getFleetByID($fleetId);
		return undef unless $fleet;
		my ($tx, $ty) = map {$fleet->getProp($_)} qw(x y);
		return undef unless $tx == $x && $ty == $y;
	}
	else
	{
		return undef unless $self->getEmpire()->planets->get($x, $y)->getProp('my');
	}
	$self->getEmpire()->commandQueue()->enqueue(dcExplorer::server::Command->new('move_unit_to_fleet', 
		{'unit_id' => $self->getProp('id'), 'fleet_id' => $fleetId}));
	$self->{Parent}->removeUnit($self);
	my $recipient = $fleetId ? $self->getEmpire()->fleets->getFleetByID($fleetId) : $self->getEmpire()->harrison($x, $y);
	$recipient->addUnit($self);
	return 1;
}

sub getActions
{
	my $self = shift;
	return $self->{Actions};
}

sub disband
{
	my $self = shift;
	$self->getEmpire()->commandQueue()->enqueue(dcExplorer::server::Command->new('demolish_building',
		{'building_id' => $self->getProp('id')}));
	$self->{Parent}->removeUnit($self);
	# TODO: Add appropriate checks.
}

sub unpack
{
	my $self = shift;
	# TODO: Implement.
	die ref($self)."::unpack is not implemented yet.";
}

# Scripting interface extension

sub transferToNewFleet
{
	my $self = shift;
	my $fleetName = shift;
	my ($x, $y) = map {$self->{Parent}->getProp($_)} qw(x y);
	return undef unless $self->getEmpire()->planets->get($x, $y)->getProp('my');
	return undef if $self->{Parent} ne $self->getEmpire()->harrison($x, $y);

	$self->getEmpire()->commandQueue()->enqueue(dcExplorer::server::Command->new('create_fleet_from_choosen', {
		'nf_check_'.$self->getProp('id') => 1, 
		'new_fleet_name' => $fleetName,
		'planet_id' => $x.":".$y,
		'fleetx' => $x,
		'fleety' => $y
	}));

	$self->{Parent}->removeUnit($self);

	my $recipient = dcExplorer::core::Fleet->new($self->getEmpire->fleets, $x, $y);
	$recipient->addUnit($self);

	return 1;
}

1;
